#lang racket

;; problem set 9

(require rackunit)
(require rackunit/text-ui)
(require 2htdp/universe)   
(require 2htdp/image)

(provide World%)
(provide Rectangle%)
(provide make-world)
(provide run)
(provide World<%>)
(provide Rectangle<%>)

;; ALGORITM: 
;; 1. Initially the world has only the target which is placed at the 
;;    center of the canvas.
;;
;; 2. On pressing "n" a new rectangle is created with its center located at
;;    the center of the target and starts moving right at a constant speed.
;;
;; 3. The target and rectangles are draggable within the canvas.
;;
;; Start with (run frame-rate speed).  Typically: (run 0.25 10)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; A NewMouseEvent is a MouseEvent and is one of the following :-
; -- "button-down"
;    Interp: on button down, the object is selected 
; -- "drag"
;    Interp: the object is dragged on the canvas
; -- "button-up"
;    Interp: the object is deselected
; -- any other mouse event
;    Interp: ignore all other mouse events
;
; Template:
; mouse-fn : NewMouseEvent -> ??
;(define (mouse-fn mev)
;  (cond [(mouse=? mev "button-down") ...]
;        [(mouse=? mev "drag") ...]
;        [(mouse=? mev "button-up") ...]
;        [else ...]))

; A NewKeyEvent is a KeyEvent and is one of 
; -- "n"        
;    Interp: when the n key is pressed, a new rectangle is added in the world
; -- any other key event
;    Interp: Ignore all other key events
;
; Template:
;key-fn : NewKeyEvent -> ??
;(define (key-fn kev) 
;  (cond [(key=? kev "n") ...]
;        [else ...]))  


;  ListOf<Rectangle<%>> is either
; - empty 
; Interp : represents a empty list of objects 
;          that implement rectangle interface
; -(cons Rectangle<%> ListOf<Rectangle<%>>)
; Interp : represents a sequence where the first element is the object
;          that implements rectangle interface and rest of the elements are 
;          represented by ListOf<Rectangle<%>>
;
; Template :
; lst-rectangle-fn :  ListOf<Rectangle<%>> -> ??
;(define (lst-rectangle-fn lst-rect)
;  (cond
;    [(empty? lst-rect) ...]
;    [else (...(first lst-rect)
;              (lst-rectangle-fn (rest lst-rect)))]))


; TargetData is a (list Number Number Boolean Number Number)
; Interp : represents the fields values of a target 
;          containing x-cordinate, y-cordinate, selected?
;          pointer-x and pointer-y (mouse cordinates)
; Template:
; target-data-fn : TargetData -> ??
;(define (target-data-fn target-data)
;  (... (first target-data)
;       (second target-data)
;       (third target-data)
;       (fourth target-data)
;       (fifth target-data))

; RectangleData is a (list Number Number Number Boolean Number Number)
; Interp : represents the fields values of a target 
;          containing x-cordinate, y-cordinate, speed, selected?
;          pointer-x and pointer-y (mouse cordinates)
; Template:
; rectangle-data-fn : RectangleData -> ??
;(define (rectangle-data-fn rectangle-data)
;  (... (first rectangle-data)
;       (second rectangle-data)
;       (third rectangle-data)
;       (fourth rectangle-data)
;       (fifth rectangle-data)
;       (sixth rectangle-data))


; a CenterData is a (list Number Number)
; Interpretaion :
; a list containing x and y co-ordinates of the center of target
;
; Templete :
; center-data-fn : CenterData -> ??
;(define (center-data-fn center-data)
;  (...(first center-data)
;      (second center-data))

; MyColor is a String and is either :
; - "Green"
; Interp : The color of the rectangle is green outline.
; - "Red"
; Interp  The color of the rectangle is red outline.
;
; Templete :
;(define (mycolor-fn mycolor)
;  (cond
;    [(string=? mycolor "green") ...]
;    [(string=? mycolor "red") ...]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; constants used for testing

(define EMPTY-CANVAS (empty-scene 400 500))

;; shape of target
(define OUT-CIRCLE (circle 10 "outline" "red"))

;; height of the target
(define CIRCLE-HALF-HEIGHT (/ (image-height OUT-CIRCLE) 2))

;; width of the target
(define CIRCLE-HALF-WIDTH  (/ (image-width  OUT-CIRCLE) 2))

;; default value of mouse x cordinate
(define POINTER-X 0)

;; default valur of mouse y -cordinate
(define POINTER-Y 0)

;; canvas width 
(define CANVAS-WIDTH 400)

;; canvas height
(define CANVAS-HEIGHT 500)

;; initial value of x cordinate of the center of target 
(define INIT-X (/ CANVAS-WIDTH 2))

;; initial value of y cordinate of the center of target 
(define INIT-Y (/ CANVAS-HEIGHT 2))

;; image of RECTANGLE
(define OUT-RECT (rectangle 30 20 "outline" "green"))

;; height of rectangle
(define RECT-HALF-HEIGHT (/ (image-height OUT-RECT) 2))

;; width of rectangle
(define RECT-HALF-WIDTH  (/ (image-width  OUT-RECT) 2))

;; default positive speed of the rectangle 
;; used for testing
(define GIVEN-SPEED 10)

;; default negative speed of the rectangle 
;; used for testing
(define NEG-SPEED -10)


;; for accessing the filds of CenterData
(define (make-centerdata x y) (list x y))
(define (centerdata-x cd) (first cd))
(define (centerdata-y cd) (second cd))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; INTERFACES :
;; Any class that implements the World<%> will support 
;; the methods of this interface.
(define World<%>
  (interface ()
    
    ;; -> Void
    ;; EFFECT: updates this World to its state following a tick
    on-tick                             

    ;; Number Number MouseEvent -> Void
    ;; EFFECT: updates this World to its state following the given
    ;; MouseEvent;
    on-mouse
    
    ;; KeyEvent -> Void
    ;; EFFECT: updates this World to its state following the given
    ;; Key event.
    on-key
    
    ;; Scene -> Scene
    ;; Returns a Scene like the given one, but with this object drawn
    ;; on it.
    add-to-scene  
    
    ;; -> Number
    ;; Returns the x coordinate of the center of target
    get-x
    
    ;; -> Number
    ;; Returns the y coordinate of the center of target
    get-y
    
    ;; -> Boolean
    ;; Returns true iff the target is selected.
    get-selected?
    
    
    ;; -> ListOf<Rectangle<%>>
    ;; Returns a list of rectangle, present in the World<%>
    get-rectangles
    
    ))

;; Any class that implements the Rectangle<%> will support 
;; the methods of this interface.
(define Rectangle<%>
  (interface ()
    
    ;; -> Void
    ;; EFFECT: updates this Rectangle to its state following a tick
    on-tick                             
    
    ;; Number Number MouseEvent -> Void
    ;; EFFECT: updates this Rectangle to its state following the given
    ;; MouseEvent;
    on-mouse
    
    ;; KeyEvent -> Void
    ;; EFFECT: updates this Rectangle to its state following the given
    ;; Key event.
    on-key
    
    ;; Scene -> Scene
    ;; Returns a Scene like the given one, but with this object drawn
    ;; on it.
    add-to-scene
    
    ;; -> Number
    ;; Returns the x coordinate of the center of rectangle
    get-x
    
    ;; -> Number
    ;; Returns the y coordinate of the center of rectangle
    get-y
    
    ;; -> Boolean
    ;; Returns true iff the rectangle is selected.
    is-selected? 
    
    ;; -> String
    ;; Returns the color in which this rectangle would be displayed if
    ;; it were displayed now.
    get-color
    
    ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; CLASSES

;; A World is a 
;; (new World% [target Target][rectangles ListOf<Rectangle<%>>][speed Number])
;; Interpretation: 
;; represents a world, containing a target, 
;; list of rectangles and an initial speed.
;; The target doesnot move on tick but can be dragged inside the canvas,
;; whereas the rectangles move with a speed and also can be dragged
;; inside the canvas. When touching the edge, the rectangles bounce and 
;; travel with the same speed in the opposite direction.
(define World%
  (class* object% (World<%>)
    
    ; represents a Target present in the World 
    (init-field target)
    
    ; represnts a list of rectangles that are created.   
    (init-field rectangles)
    
    ; represents the speed at which the rectangles should move
    ; in the world
    (init-field speed)
    
    (super-new) 
    
    ; -> Void
    ; EFFECT: returns a World after a tick event where
    ; target stays at its position when not dragged,
    ; and rectangles move by a given speed in a horizontal direction,
    ; initially towards right
    ; Example : See tests below
    ; Strategy : Higher Order Function composition
    (define/public (on-tick)
      (for-each
       (lambda (rectangle) (send rectangle on-tick))
       rectangles))
    
    ; Number Number NewMouseEvent -> Void
    ; EFFECT: given x and y cordinates of the mouse and a mouse event,
    ; performs on-mouse on every object.If the mouse co-ordinates are inside
    ; the target or rectangle or both, then updates the world with
    ; setting the fields of the objects.
    ; Example : See tests below
    ; Strategy : Structural Decomposition on this 
    ; + Higher Order Function composition
    (define/public (on-mouse x y mev)
      (send target on-mouse x y mev)
      (for-each
       (lambda (rectangle) (send rectangle on-mouse x y mev))
       rectangles))
    
    
    ; NewKeyEvent -> Void
    ; EFFECT: given a keyevent, passes the on-key to all objects
    ; and the effect on every stateful objects is performed.
    ; Example : See tests below
    ; Strategy: Structural decomposition on NewKeyEvent (kev)
    (define/public (on-key kev)
      (cond
        [(key=? kev "n")
         (set! rectangles (cons (create-rect) rectangles))]
        [else this]))
    
    
    ; Scene -> Scene 
    ; takes in a a scene and renders
    ; the target and rectangles on the given scene and returns the scene
    ; Example  : See tests below
    ; Strategy : Structural decomposition on this
    ; + Higher Order function composition
    (define/public (add-to-scene scene0)
      (local (; -> Scene  
              ;returns a scene after adding the target
              ;onto the canvas.
              ;Strategy : Structural decomposition on this
              (define scene-with-target 
                (send target add-to-scene scene0)))
        (foldr
         (lambda (rectangle scene) (send rectangle add-to-scene scene))
         scene-with-target rectangles)))
    
     
    ; -> Rectangle%
    ; creating the Rectangle at the position of the Target
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (create-rect)
      (local
        ((define the-center (send target center)))
         (new Rectangle% 
              [x (centerdata-x the-center)]
              [y (centerdata-y the-center)]
              [speed speed]
              [pointer-x POINTER-X] 
              [pointer-y POINTER-Y])))
    
    ; -> Target%
    ; returns the target object
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-target) 
      target)
    
    ; -> ListOf<Rectangle<%>>
    ; returns a list of rectangle created till now in the world
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-rectangles) 
      rectangles)
    
    ; -> Number
    ; returns the x-cordinate of the center of target
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-x) 
      (send target get-x))
    
    ; -> Number
    ; returns the y-cordinate of the center of target
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-y) 
      (send target get-y))
    
    ; -> Boolean
    ; returns true iff the target is selected else returns false
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-selected?) 
      (send target get-selected?))
    
    ; -> (list Target ListOf<Rectangle<%>>)
    ; returns a list containing target and a list of rectangles
    ; used for testing
    ; Strategy : Domain Knowledge
    (define/public (probe) 
      (list target rectangles speed))
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; A Rectangle is a 
;;(new Rectangle% [x Number][y Number]
;;                [speed Number][selected? Boolean] 
;;                [pointer-x Number][pointer-y Number])
;; Interpretation: 
;; represents a rectangle, in the world with its center at x and y,
;; and which moves with a speed inside the world. The recangle can be dragged
;; inside the canvas and is created at the cordinates of the center of
;; the target.
(define Rectangle%
  (class* object% (Rectangle<%>) 
    
    (init-field
     ; the rectangle's x position, in pixels, 
     ; relative to the upper-left corner of the canvas
     x  
     ; the rectangle's y position
     ; relative to the upper-left corner of the canvas
     y )         
    
    ; the field pointer-x represents the value of x-cordinate of mouse
    ; when the target is selected, when unselected retains the value of
    ; mouse when last clicked.
    (init-field pointer-x) 
    
    ; the field pointer-y represents the value of y-cordinate of mouse
    ; when the target is selected, when unselected retains the value of
    ; mouse when last clicked.
    (init-field pointer-y)
    
    ; the speed of the rectangle
    (init-field speed)
    
    ; the rectangle's selected? status
    ; initially false.
    (init-field [selected? false])
    
    ; represents the rectangle's color status
    ; color defaults to green.
    (init-field [rect-color "green"])
    
    ; image of the rectangle
    (field [IMG (rectangle 30 20 "outline" "green")])     
    
    (super-new)
    
    ; -> Void
    ;EFFECT: sets the value of x cordinate and the speed of the 
    ; rectangle after a tick event,
    ;  - if the rectangle is selected then retains the same state of rectangle
    ;  - if the rectangle is at right edge or will cross right edge 
    ;    then updates the rectnagle state by changing the x-corinate and 
    ;   making it alligned to the right edge of canvas in next tick
    ;  - if the rectangle is at left edge or will cross the left edge in the
    ;    next ic event then, updates the state of the rectnagle by changing 
    ;    the x-coordinate and making it alligned to the left edge of canvas
    ;  - at any position inside the canvas updates the state by changing the
    ;    x cordinate of the rectangle depending upon the value of speed with
    ;    which the rectangle is movig, either towards left or right.
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (on-tick)
      (local (;Number -> Rectangle
              ;returns a rectangle after the value of x-ordinate is provided
              ;Strategy : Domain Knowledge
              (define (create-rect new-x)
                (set! x new-x)))
        (cond
          [selected? (create-rect x)]
          [(send this touching-right-edge?) (send this update-at-right-edge)]
          [(send this crossing-right-edge?)
           (send this update-after-right-edge)]
          [(send this is-rect-at-left-edge?) (send this new-pos-at-left-edge)]
          [(send this is-rect-crossing-left-edge?)
           (send this new-pos-after-left-edge)]
          [else (create-rect (+ x speed))])))
    
    
    ; -> Boolean
    ; returns true iff the given rectangle is touching the right edge of
    ; canvas else false.
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (touching-right-edge?)
      (= x (- CANVAS-WIDTH RECT-HALF-WIDTH)))
    
    ; -> Void
    ; EFFECT: sets the value of x-cordinate of the rectangle after a 
    ; tick event if the rectangle is touching right edge of the canvas
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (update-at-right-edge)
      (begin
        (set! x (- x (send this right-direction))) 
        (set! speed (send this left-direction))))
    
    ; -> Number
    ; returns the same value if speed is positive else returns 
    ; a positive value of speed
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (right-direction)
      (if (positive? speed) speed (- speed)))
    
    ; -> Number
    ; returns the same value if speed is negative else returns 
    ; a negative value of given speed
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (left-direction)
      (if (negative? speed) speed (- speed)))
    
    ; -> Boolean
    ; returns true iff the rectangle will cross the right edge after 
    ; the tick event
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (crossing-right-edge?)
      (and (positive? speed)
           (> (+ x speed) (- CANVAS-WIDTH RECT-HALF-WIDTH))))
    
    ; -> Void
    ; EFFECT : sets the value of x-cordinate of the rectangle and the speed
    ; after tick, when the rectangle will
    ; cross the right edge on next tick
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (update-after-right-edge)
      (begin
        (set! x (- CANVAS-WIDTH RECT-HALF-WIDTH)) 
        (set! speed (send this left-direction))))
    
    ; -> Boolean
    ; returns true iff the rectangle is at left edge
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-rect-at-left-edge? )
      (= x RECT-HALF-WIDTH))
    
    ; -> Void
    ; EFFECT: sets the x-cordinate and the speed of the rectnagle after tick,
    ; when the rectangle will touch the left edge on next tick
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (new-pos-at-left-edge)
      (begin
        (set! x (+ x (send this right-direction)))
        (set! speed (send this right-direction))))
    
    ; -> Boolean
    ; returns true iff the rectangle will cross the left edge 
    ; of the canvas on next tick event
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-rect-crossing-left-edge?)
      (and (negative? speed)
           (< (- x (- speed)) RECT-HALF-WIDTH)))
    
    ; -> Void
    ; EFFECT : sets the x-cordinate and the speed of the rectnagle after tick,
    ; the rectangle starts moving right after touching the left edge
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (new-pos-after-left-edge)
      (begin
        (set! x RECT-HALF-WIDTH) 
        (set! speed (send this right-direction))))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ; NewKeyEvent -> Void
    ; EFFECT: a rectangle ignores key event, its state remains unchanged
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (on-key kev)
      this)      
    
    ; Number Number NewMouseEvent -> Void
    ; EFFECT: takes in the x and y co-ordinates of the mouse and 
    ; applies the mouse event to all the stateful objects
    ; Example : See tests below
    ; Strategy : Structural decomposition on evt : [NewMouseEvent]
    (define/public (on-mouse mouse-x mouse-y evt)
      (cond 
        [(mouse=? evt "button-down")
         (send this rect-after-button-down mouse-x mouse-y)]
        [(mouse=? evt "drag") 
         (send this set-new-pos-on-drag mouse-x mouse-y)]
        [(mouse=? evt "button-up")
         (send this rect-after-button-up)]
        [else this]))
    
    
    ; Number Number -> Void
    ; EFFECT: if the mouse coordinates lie inside the rectnagle
    ; then select the rectnagle, else the rectangle remains unaffected
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (rect-after-button-down mouse-x mouse-y)
      (if (send this pnt-in-rect? mouse-x mouse-y)
          (begin(set! selected? true)
                (set! pointer-x mouse-x) 
                (set! pointer-y mouse-y))
          this))
    
    ; Number Number -> Void
    ; EFFECT: takes the cordinates of mouse, and the rectangle after
    ; it has been dragged relative to the mouse
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (set-new-pos-on-drag mx my)
      (if selected?
          (begin(set! x (send this new-pos-x-right-left-edge mx))
                (set! y (send this new-pos-y-top-bottom-edge my))
                (set! selected? true)
                (set! pointer-x mx)
                (set! pointer-y my))
          this))
         
    ; Number -> Number
    ; takes the x and y cordinates of mouse, and sets the x-cordinate of 
    ; the rectangle after it has been dragged relative to the mouse
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (new-pos-x-right-left-edge mx)
      (- mx (- pointer-x x)))
    
    
    ; Number -> Number 
    ; takes the y-cordinate of the mouse and returns the y-cordinate 
    ; of the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (new-pos-y-top-bottom-edge my)
      (- my (- pointer-y y)))
    
    ; -> Void
    ; EFFECT : on a button up mouse event, the rectnagle, 
    ; if selected is unslected.
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (rect-after-button-up)
      (set! selected? false))
     
    ; Scene -> Scene
    ; takes a scene and returns a scene after placing the image 
    ; of rectangle onto the given scene
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (add-to-scene scene)
      (place-image IMG x y scene))
    
    ; Number Number -> Boolean
    ; returns true iff the mouse co-ordinates are inside the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (pnt-in-rect? pointer-x pointer-y)
      (and
       (< (- x RECT-HALF-WIDTH) pointer-x (+ x RECT-HALF-WIDTH))
       (< (- y RECT-HALF-HEIGHT) pointer-y (+ y RECT-HALF-HEIGHT))))
    
    ;; -> String
    ;; Returns the color in which this rectangle would be displayed if
    ;; it were displayed now.
    (define/public (get-color)
      rect-color)
    
    ;; getters used for testing:
    
    ; -> Number
    ; returns the x-cordinate of the center of rectangle
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-x) 
      x)
    
    ; -> Number
    ; returns the y-cordinate of the center of rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-y)  
      y) 
    
    ; -> Number
    ; returns the speed of the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-speed) 
      speed)
    
    ; -> Boolean
    ; returns true iff the rectangle s selected, else false
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-selected?) 
      selected?)
    
    ; -> Number
    ; returns the x-cordinate of the mouse when rectangle is selected
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-pointer-x) 
      pointer-x)
    
    ; -> Number
    ; returns the y-cordinate of the mouse when rectangle is selected
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-pointer-y) 
      pointer-y)
    
    ; -> RectangleData
    ; returns a list consisting of fields of the rectangle
    ; used ofr testing
    ; Strategy : Domain Knowledge
    (define/public (probe) 
      (list x y speed selected? pointer-x pointer-y))
    
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; A Target is a 
;;(new Target% [x Number][y Number]
;;             [selected? Boolean]
;;             [pointer-x Number][pointer-y Number])
;; A Target represents a target whos center has x and y cordinates.
;; The target can be dragged inside the canvas.
(define Target%
  (class* object% ()
    (init-field
     x ; the x position of center of target
     y ; the y position of center of target
     )
    
    ; the field pointer-x represents the value of x-cordinate of mouse
    ; when the target is selected, when unselected retains the value of
    ; mouse when last clicked.
    (init-field pointer-x)
    
    ; the field pointer-y represents the value of y-cordinate of mouse
    ; when the target is selected, when unselected retains the value of
    ; mouse when last clicked.
    (init-field pointer-y)
    
    ; the field selected? represents if the target is selected or not
    (init-field selected?)
    
    ; image of target
    ; represents the image of the target
    (field [TARGET-IMG (circle 10 "outline" "red")])
    
    (super-new)
    
    ; Number Number NewMouseEvent -> Void
    ; EFFECT: takes in the x and y co-ordinates of the mouse and 
    ; applies the mouse event the stateful target object
    ; Example : See tests below
    ; Strategy : Structural decomposition on evt : [NewMouseEvent]
    (define/public (on-mouse mouse-x mouse-y evt)
      (cond
        [(mouse=? evt "button-down")
         (send this target-after-button-down mouse-x mouse-y)]
        [(mouse=? evt "drag") 
         (send this set-new-pos-on-drag mouse-x mouse-y)]
        [(mouse=? evt "button-up")
         (send this target-after-button-up)]
        [else this]))
    
    ; Number Number -> Void
    ; EFFECT: if the button down event happens with
    ; mouse coordinates is inside the target
    ; then select the target, else the target remains unaffected
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (target-after-button-down mouse-x mouse-y)
      (if (send this in-target? mouse-x mouse-y)
          (begin
            (set! pointer-x mouse-x)
            (set! pointer-y mouse-y)
            (set! selected? true))
          this))
    
    ; Number Number -> Void
    ; EFFECT: when the mouse drag happens, with target selected then
    ; the mouse x and y co-ordinates is used to move the target to that 
    ; position relatively, if the target is not selected then target 
    ; remains unchanged.
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (set-new-pos-on-drag mx my)
      (if selected?
          (begin
            (set! x (send this new-pos-x-right-left-edge-target mx))
            (set! y (send this new-pos-y-top-bottom-edge-target my))
            (set! selected? true) 
            (set! pointer-x mx) 
            (set! pointer-y my))
          this))
    
    ; Number -> Number
    ; takes the x-cordinate of the mouse and returns the x-cordinate of the
    ; target after the target is moved relatively.
    ; Example : See tests below
    ; Startegy : Domain Knowledge
    (define/public (new-pos-x-right-left-edge-target mx)
      (- mx (- pointer-x x)))
    
    ; Number -> Number
    ; takes the y-cordinate of the mosue and returns the y-cordinate of the
    ; target after the target has been moved relatively.
    ; Example : See tests below
    ; Startegy : Domain Knowledge
    (define/public (new-pos-y-top-bottom-edge-target my)
      (- my (-  pointer-y y)))
    
    ; -> Void
    ; EFFECT : on a button up mouse event,
    ; the target, if selected is unslected.
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (target-after-button-up)
      (set! selected? false))
    
    ; Number Number -> Boolean
    ; takes the x and y cordinates of the mouse and returns true iff the
    ; mouse cordinates are within the target, else false
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (in-target? mx my)
      (and 
       (< (- x CIRCLE-HALF-WIDTH) mx (+ x CIRCLE-HALF-WIDTH))
       (< (- y CIRCLE-HALF-WIDTH) my (+ y CIRCLE-HALF-WIDTH))))
    
    ; Scene -> Scene
    ; takes a scene and returns a new scene after placing the image of target
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (add-to-scene scene)
      (place-image TARGET-IMG x y scene))
    
    ; -> CenterData
    ; Adds the values of fields x and y to the list, which is maintained
    ; outside the class.
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (center) 
      (make-centerdata x y))
    
    ; getters: used only for testing
    ; -> Number
    ; returns the x cordinate of the center of target 
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-x) x)
     
    ; -> Number
    ; returns the y cordinate of the center of target 
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-y) y)
    
    ; -> Boolean
    ; returns true if the target is selected else false
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-selected?) selected?)
    
    ; -> Number
    ; returns the x cordinate of the mouse when target is selected
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-pointer-x) pointer-x)
    
    ; -> Number
    ; returns the y cordinate of the mouse when target is selected
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-pointer-y) pointer-y)
    
    )) 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; run : Number Number -> World%
;; takes in the frame rate (secs/tick), and speed ,determines the speed
;; with which rectangles move horizontally,initially towards right and 
;; returns a world
;; which has a target placed at the center of the canvas and any rectangle
;; added to the world. 
;; Example : 
;; (run .25 10) -> produces  a world which has a target placed at the center
;; with no rectangle.
;; INVARIANT : the given speed is > 0
;; Strategy : Function Composition
(define (run frame-rate given-speed)
  (big-bang (make-world given-speed)
            (on-tick (lambda (w) (send w on-tick) w) frame-rate)
            (on-draw (lambda (w) (send w add-to-scene EMPTY-CANVAS)))
            (on-key (lambda (w kev) (send w on-key kev) w))
            (on-mouse (lambda (w x y evt) (send w on-mouse x y evt) w))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;; make-world : Number -> World%
;; takes in a the speed with which the rectangles move inside the world
;; and returns a world, where rectangles have not been added, but any
;; rectangles added in future will move with the given speed, horizontally,
;; and initially towards right and 
;; with target placed at the center of the canvas.
;; Example : See tests below 
;; Strategy : Function Composition
(define (make-world given-speed)
  (new World% 
       [target (new Target% [x INIT-X][y INIT-Y]
                    [selected? false][pointer-x POINTER-X]
                    [pointer-y POINTER-Y])]
       [rectangles empty]
       [speed given-speed]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define target-1
  (new Target%
       [x INIT-X] [y INIT-Y]
       [selected? #t] 
       [pointer-x POINTER-X] [pointer-y POINTER-Y]))
(define rect1
  (new Rectangle%
       [x (+ 20 INIT-X)] [y INIT-Y]
       [speed  GIVEN-SPEED] 
       [selected? #f] 
       [pointer-x POINTER-X] [pointer-y POINTER-Y]))
(define rect2
  (new Rectangle%
       [x (+ 100 INIT-X)] [y (+ 50 INIT-Y)]
       [speed  GIVEN-SPEED]
       [selected? #f] 
       [pointer-x POINTER-X] [pointer-y POINTER-Y]))

 
(test-case
 "test"
 (define w (make-world 10))
 (define testworld
  (new World% 
       [target target-1]
       [rectangles (list rect1 rect2)] 
       [speed 10]))
 (send w on-mouse INIT-X INIT-Y "button-down")
 (send w on-mouse (+ 100 INIT-X) INIT-X "drag")
 (send w on-mouse (+ 100 INIT-X) 100 "button-up")
(define test1 (first (second (send testworld probe))))
 (check-equal? (send test1 get-x)
               220
               "aaa")
 (check-equal? (send test1 get-color)
               "green"
               "test for color of rectangle")
 (check-equal? (send test1 is-selected?)
               #f
               "test if selcted or not")
 (check-equal? (send test1 get-speed)
               10
               "test for speed")
                     
 ) 
;; button down and drag betond right edge
(define rect3
  (new Rectangle% 
       [x (+ 15 INIT-X)] [y (* 1 INIT-Y)]
       [speed  GIVEN-SPEED]
       [selected? #f] 
       [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(define rect4
  (new Rectangle% 
       [x (+ 100 INIT-X)] [y (* 1 INIT-Y)]
       [speed  GIVEN-SPEED]
       [selected? #f] 
       [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(test-case
 "test2"
 (define testworld
  (new World%  
       [target target-1] 
       [rectangles (list rect3 rect2)] 
       [speed 10]))
 (send testworld on-mouse (+ 15 INIT-X) INIT-Y "button-down")
 (send testworld on-mouse 415 INIT-Y "drag")
 (send testworld on-tick)
(define test1 (first (second (send testworld probe))))
 (check-equal? (first (send test1 probe))
               415
               "aaa") 
 
 (send testworld on-mouse (+ 15 INIT-X) INIT-Y "button-up")
 (check-equal? (fourth (send test1 probe))
               #f
               "aaa")
 (send testworld on-mouse (+ 15 INIT-X) INIT-Y "move")
 (check-equal? (send test1 probe)
                     
               (list 415 250 10 #f 415 250)
               "aaa")
 (send testworld on-key "d")
 (check-equal? (send test1 probe)
                     
               (list 415 250 10 #f 415 250)
               "aaa")
 (define rect-on-key (send rect4 on-key "n"))
 (check-equal? (send test1 probe)
                     
               (list 415 250 10 #f 415 250)
               "aaa")
 
 )
 
;; test for rectangle motion near the edges
(test-case
 "test3"
 (define testworld1
  (new World% 
       [target target-1] 
       [rectangles (list rect4 rect2)]
       [speed 10]))
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-down")
 (send testworld1 on-mouse 0 INIT-Y "drag")
 (send testworld1 on-tick)
(define test1 (first (second (send testworld1 probe))))
 (check-equal? (first (send test1 probe))
               0
               "for left edge")
 
 (send testworld1 on-mouse 372 INIT-Y "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (first (send test1 probe))
               382
               "for right edge")
 (send testworld1 on-mouse 385 INIT-Y "button-down")
 (send testworld1 on-mouse 16 INIT-Y "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (first (send test1 probe))
               23
               "for left edge, pure alligned")
 
 (send testworld1 on-mouse 15 INIT-Y "button-down")
 (send testworld1 on-mouse 385 INIT-Y "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (first (send test1 probe))
               385
               "just right")
  
 (send testworld1 on-mouse 375 INIT-Y "button-down")
 (send testworld1 on-mouse 15 INIT-Y "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (first (send test1 probe))
               15
               "just left")
 
 (send testworld1 on-mouse 25 INIT-Y "button-down")
 (send testworld1 on-mouse 15 800 "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (second (send test1 probe))
               800
               "just bottom")
 
 (send testworld1 on-mouse 15 485 "button-down")
 (send testworld1 on-mouse 15 -100 "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (check-equal? (second (send test1 probe))
               800
               "just up")
 
 (send testworld1 on-mouse 15 15 "button-down")
 (send testworld1 on-mouse 385 40 "drag")
 (send testworld1 on-mouse (+ 100 INIT-X) INIT-Y "button-up")
 (send testworld1 on-tick)
 (send testworld1 on-tick)
 (send testworld1 on-tick)
 (check-equal? 
  (list (send test1 get-pointer-x)
       (send test1 get-pointer-y)
       (send test1 get-x)
       (send test1 get-y)
       (send test1 is-selected?))
               '(15 800 55 800 #f)
               "getter values")
 (send testworld1 on-key "n")
 (send testworld1 add-to-scene EMPTY-CANVAS)
 (define targ (send testworld1 get-target))
 (define getrect (send testworld1 get-rectangles))
 (check-equal?
  
 (list 
   (send targ get-x)
   (send targ get-y)
   (send targ get-selected?) 
   (send targ get-pointer-x)
   (send targ get-pointer-y))
 '(615 500 #f 415 250)
 "getter method, get-target for world")
 (define get-x-y-slec (list (send testworld1 get-x)
                    (send testworld1 get-y)
                    (send testworld1 get-selected?))
   ) 
 (check-equal?
 get-x-y-slec
 '(615 500 #f)
 "getter method, get x for world, fethches the x for target")
 )
 